package edu.neu.cs6515.net;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.LinkedList;
import java.util.List;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;

import com.google.common.collect.Lists;

import edu.neu.cs6515.admin.Administrator;
import edu.neu.cs6515.xml.IngeniousColor;
import edu.neu.cs6515.xml.Tile;
import edu.neu.cs6515.xml.w12.Accept;
import edu.neu.cs6515.xml.w12.Register;

/**
 * server of the distributed game
 */
public class Server {

    public static final String ADDR = "localhost";
    private List<Socket> socks = new LinkedList<Socket>();

    public static final int PORT = 12843;

    /**
     * run the server
     * 
     * @param port
     * @param i
     */
    public void runServer(final int nbrOfPlayers, final int port) throws IOException {
        System.out.println("server listenning at " + port);
        ServerSocket srvSock = new ServerSocket(port);
        Administrator admin = new Administrator(nbrOfPlayers);
        socks = waitAndRegisterPlayers(srvSock, admin);
        System.out.println(admin.runGame());
        cleanUpSockets(srvSock);
    }

    /**
     * wait untill sufficient number of players registered
     * 
     * @param srvSock
     * @param admin
     * @return list of sockets connected
     */
    public List<Socket> waitAndRegisterPlayers(final ServerSocket srvSock, final Administrator admin) {
        List<Socket> retSocks = new LinkedList<Socket>();
        while (!admin.enoughPlayerRegistred()) {
            Socket sock = new Socket();
            try {
                sock = srvSock.accept();
                ProxySocketInOutput inOut = new ProxySocketInOutput(sock);
                onConnect(admin, inOut);
                retSocks.add(sock);
            } catch (Throwable e) {
                e.printStackTrace();
                cleanSocket(sock);
            }
        }
        return retSocks;
    }

    /**
     * close all sockets
     * 
     * @param srvSock
     */
    private void cleanUpSockets(final ServerSocket srvSock) {
        for (Socket sock : socks) {
            cleanSocket(sock);
        }
        try {
            srvSock.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * close given socket
     * 
     * @param sock
     */
    private void cleanSocket(final Socket sock) {
        try {
            sock.shutdownInput();
            sock.shutdownOutput();
            if (!sock.isClosed()) {
                sock.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * event handler of incomming connection
     * 
     * @param admin
     * @param inOut
     * @throws InputOutputException
     */
    public void onConnect(final Administrator admin, final IProxyInOutput inOut)
            throws InputOutputException {
        Object obj = inOut.receive();
        Register reg = (Register) obj;
        PlayerProxy playerProxy = new PlayerProxy(reg.getName(), inOut);
        admin.registerPlayer(playerProxy, reg.getName());
    }

    public static void main(final String[] args) {
        try {
            new Server().runServer(4, 56789);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
